/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.model;

import java.util.HashMap;
import javax.servlet.http.HttpSession;

/**
 * A generic in memory cache.
 *
 * @author Walter Lütgenau
 */
public class Cache {

    static HashMap<String, CacheEntry> globalCaches = new HashMap<String, CacheEntry>();
    static final long GLOBAL_INVALIDATION_TIME = 10000; // 5 minutes
    static final long SESSION_INVALIDATION_TIME = GLOBAL_INVALIDATION_TIME; 
    static final String SESSION_CACHES = "sessionCaches";

    /**
     * A cache entry. It will store the modification time
     */
    static class CacheEntry {

        Object cacheObject;
        long modificationTime;
        long invalidationTime;

        public CacheEntry(Object cacheObject, long invalidationTime) {
            this.cacheObject = cacheObject;
            this.invalidationTime = invalidationTime;
            this.modificationTime = System.currentTimeMillis();
        }

        public Object getCacheObject() {
            return cacheObject;
        }

        public void setCacheObject(Object cacheObject) {
            this.cacheObject = cacheObject;
        }

        public long getInvalidationTime() {
            return invalidationTime;
        }

        public void setInvalidationTime(long invalidationTime) {
            this.invalidationTime = invalidationTime;
        }

        public long getModificationTime() {
            return modificationTime;
        }

        public void setModificationTime(long modificationTime) {
            this.modificationTime = modificationTime;
        }

        protected boolean isValid() {
            long currentTime = System.currentTimeMillis();
            if (currentTime - modificationTime > invalidationTime)
                return false;

            modificationTime = currentTime;
            return true;
        }
    }

    protected static synchronized HashMap<String, CacheEntry> getCaches(HttpSession session) {
        HashMap<String, CacheEntry> caches = (HashMap<String, CacheEntry>) session.getAttribute(SESSION_CACHES);
        if (caches == null) {
            caches = new HashMap<String, CacheEntry>();
            session.setAttribute(SESSION_CACHES, caches);
        }

        return caches;
    }

    public static Object getObject(HttpSession session, String name, CacheObjectFactory cacheObjectFactory) {
        return getObject(getCaches(session), name, SESSION_INVALIDATION_TIME, cacheObjectFactory);
    }

    public static Object getObject(String name, CacheObjectFactory cacheObjectFactory) {
        return getObject(globalCaches, name, GLOBAL_INVALIDATION_TIME, cacheObjectFactory);
    }

    protected static Object getObject(HashMap<String, CacheEntry> caches, String name, CacheObjectFactory cacheObjectFactory) {
        return getObject(caches, name, GLOBAL_INVALIDATION_TIME, cacheObjectFactory);
    }

    public static Object getObject(HttpSession session, String name, long invalidationTime, CacheObjectFactory cacheObjectFactory) {
        return getObject(getCaches(session), name, invalidationTime, cacheObjectFactory);
    }

    public static Object getObject(String name, long invalidationTime, CacheObjectFactory cacheObjectFactory) {
        return getObject(globalCaches, name, invalidationTime, cacheObjectFactory);
    }

    protected static synchronized Object getObject(HashMap<String, CacheEntry> caches, String name, long invalidationTime, CacheObjectFactory cacheObjectFactory) {
        CacheEntry entry = caches.get(name);
        if (entry != null && !entry.isValid())
            entry = null;
        if (entry == null) {
            entry = new CacheEntry(cacheObjectFactory.createCacheObject(), invalidationTime);
            caches.put(name, entry);
        }

        return entry.getCacheObject();
    }

    public static void invalidate(HttpSession session, String name) {
        invalidate(getCaches(session), name);
    }

    public static void invalidate(String name) {
        invalidate(globalCaches, name);
    }

    protected static synchronized void invalidate(HashMap<String, CacheEntry> caches, String name) {
        caches.remove(name);
    }

 }
